package com.zz.server.datasource;

import com.zz.common.annotation.TunnelSource;
import com.zz.common.config.Constants;
import com.zz.common.datasource.AbstractDataSource;
import com.zz.common.entity.ClientConfig;
import com.zz.common.entity.User;
import com.zz.common.entity.UserClient;
import com.zz.common.enums.ProxyType;
import com.zz.common.enums.Status;
import com.zz.common.exception.TunnelException;
import com.zz.common.utils.LockUtil;
import com.zz.common.utils.ObjectUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 默认内存数据源（仅供学习测试使用，从配置文件中获取信息）
 */
@Slf4j
@TunnelSource
public class MemoryDataCache extends AbstractDataSource {

    /**
     * 二级缓存，内存数据源读取配置文件，一次加载即可
     */
    private final static Map<String, User> userInfoCache = new ConcurrentHashMap<>();
    private final static Map<String, UserClient> userClientCache = new ConcurrentHashMap<>();
    private final static Map<String, ClientConfig> clientConfigCache = new ConcurrentHashMap<>();

    private final static Map<String, List<String>> userClientMapping = new ConcurrentHashMap<>();

    private final static Map<String, List<String>> clientConfigMapping = new ConcurrentHashMap<>();

    private final static Map<String, String> configDomainMapping = new ConcurrentHashMap<>();


    @Override
    public void initSource() {
        // 测试使用
        userInfoCache.put("1", new User("1", "zhangzhen", "zhengzhen"));
        userClientCache.put("1", new UserClient("1", "1", "zhangzhen", "zhangzhen", 5, false));
        clientConfigCache.put("1", new ClientConfig("1", "1", ProxyType.HTTP, "zz.shiyue.com", "127.0.0.1", "8080", 5, Status.OPEN));
        userClientMapping.put("1", Collections.singletonList("1"));
        clientConfigMapping.put("1", Collections.singletonList("1"));
        configDomainMapping.put("zz.shiyue.com", "1");

    }

    @Override
    public void clearSource() {
        log.info("默认数据源二级缓存无需清空");
    }

    @Override
    public User authorizeBySource(String username, String password) {
        List<User> userList = userInfoCache.values().stream().map(user -> {
            if (user.getUsername().equals(username) && user.getPassword().equals(password)) {
                return user;
            }
            return null;
        }).collect(Collectors.toList());
        return userList.size() == 1 ? userList.get(0) : null;
    }

    @Override
    public List<UserClient> getUserClientBySource(String userId) {
        return userClientMapping.get(userId).stream().map(userClientCache::get).collect(Collectors.toList());
    }

    @Override
    public List<ClientConfig> getClientConfigBySource(String clientId) {
        return clientConfigMapping.get(clientId).stream().map(clientConfigCache::get).collect(Collectors.toList());
    }

    @Override
    public User getUserById(String userId) {
        return userInfoCache.get(userId);
    }

    @Override
    public UserClient getUserClientById(String clientId) {
        return userClientCache.get(clientId);
    }

    @Override
    public ClientConfig getClientConfigById(String configId) {
        return clientConfigCache.get(configId);
    }

    @Override
    public ClientConfig getClientConfigIdByDomain(String domain) {
        return clientConfigCache.getOrDefault(configDomainMapping.getOrDefault(domain, ""), null);
    }


    @Override
    public void updateConfig(ClientConfig config) {
        if (ObjectUtil.isEmpty(config.getConfigId())) {
            return;
        }
        try {
            LockUtil.executeWithLock(config.getConfigId(), () -> {
                ClientConfig clientConfig = clientConfigCache.get(config.getConfigId());
                if (ObjectUtil.isNotEmpty(config.getConfigIp())) {
                    clientConfig.setConfigIp(config.getConfigIp());
                }
                if (ObjectUtil.isNotEmpty(config.getConfigPort())) {
                    clientConfig.setConfigPort(config.getConfigPort());
                }
                if (ObjectUtil.isNotEmpty(config.getConcurrency())) {
                    clientConfig.setConcurrency(config.getConcurrency());
                }
                return Constants.Objects.NULL;
            });
        } catch (Exception e) {
            log.error("update config fail:{}", config);
            throw new TunnelException(e.getMessage());
        }
        clear();

    }


}
